<template>
  <div v-loading="mixingStationLoading" class="app-container flex h-100% overflow-hidden">
    <div class="left w-350px">
      <el-tabs class="tabs h-100%">
        <el-tab-pane label="车辆列表">
          <div class="p-10px pt-0">
            <el-input v-model="searchValue" placeholder="关键字搜索" clearable @input="onQueryChanged" @keyup.enter="onQueryChanged">
              <template #append>
                <el-button size="small" @click="onQueryChanged">
                  <template #icon><ep-Search /></template>
                </el-button>
              </template>
            </el-input>

            <el-select v-model="queryParams.mixingStationId" class="mt-10" placeholder="请选择搅拌站" @change="handleSelectMixingStation">
              <el-option v-for="(item, index) in mixingStationOptions" :key="index" :label="item.name" :value="item.id" />
            </el-select>
          </div>

          <el-scrollbar v-loading="carListLoading" height="calc(100% - 90px)">
            <div
              v-for="(item, index) in carData"
              :key="index"
              class="wrapper_item flex-x-s px-20px py-5px cursor-pointer"
              @click="fetchCarInfo(item)"
            >
              <svg width="20" height="20" :fill="statusOptions.find(option => option.value === item.carStatus)?.color">
                <use xlink:href="#truck"></use>
              </svg>

              <span class="ml-6px">{{ item.mixingTruckNumber }}-{{ item.carNumber }}</span>
              <span class="time-box" :class="item.online === 1 ? 'text-blue' : 'text-gray'">
                /{{ item.gpsTime ? item.gpsTime : '无信号' }}
              </span>
            </div>

            <el-empty v-if="!carData.length && !carListLoading" description="暂无车辆" />
          </el-scrollbar>
        </el-tab-pane>
        <el-tab-pane label="客户列表">
          <div class="p-10px pt-0">
            <el-input
              v-model="customerSearchValue"
              placeholder="关键字搜索"
              clearable
              @input="onCustomerQueryChanged"
              @keyup.enter="onCustomerQueryChanged"
            >
              <template #append>
                <el-button size="small" @click="onCustomerQueryChanged">
                  <template #icon><ep-Search /></template>
                </el-button>
              </template>
            </el-input>
          </div>

          <el-scrollbar v-loading="customerListLoading" height="calc(100% - 50px)">
            <div
              v-for="(item, index) in customerData"
              :key="index"
              class="wrapper_item flex-x-s px-15px py-5px cursor-pointer"
              :class="{ 'item-selected': item.selected }"
              @click="handleSelectCustomer(item)"
            >
              <el-checkbox v-model="item.checked" @change="val => handleCheckedChange(val, item)" @click.stop />
              <div class="flex flex-col ml-10">
                <span>{{ item.customerName }}</span>
                <span class="project-name-text">{{ item.projectName }}</span>
              </div>
            </div>

            <el-empty v-if="!customerData.length && !customerListLoading" description="暂无客户" />
          </el-scrollbar>
        </el-tab-pane>
      </el-tabs>
    </div>
    <div class="flex-1 relative">
      <div id="map-container" class="w-100% h-100%"></div>

      <!-- 车辆状态 -->
      <div class="status-items">
        <div v-for="(statusItem, index) in vehicleStatusList" :key="index" class="status-item">
          <i :style="{ backgroundColor: statusItem.color }"></i>
          {{ statusItem.label }}
          <span>{{ statusItem.count || 0 }}</span>
        </div>
      </div>

      <div :hidden="!isLoading" class="loader-spinner"></div>

      <!-- 地址检索 -->
      <div class="absolute right-15px top-10px">
        <el-autocomplete
          v-model="keyword"
          :fetch-suggestions="handleSearch"
          :trigger-on-focus="false"
          clearable
          placeholder="地址检索"
          @select="handleSelect"
        >
          <template #prefix>
            <el-icon>
              <ep-Search />
            </el-icon>
          </template>
        </el-autocomplete>
      </div>

      <div class="hidden">
        <!-- marker点带关闭按钮 -->
        <Marker ref="markerRef" @close="clearMarker" />

        <!-- 车辆信息地图弹框 -->
        <CarInfoWindow
          ref="carInfoWindowRef"
          :track-card-id="currentTrackCardId"
          :car-info="carInfo"
          @open-waybill-detail="detailVisible = true"
          @track-vehicle="trackVehicle"
          @close="handleCloseInfoWindow"
        />
      </div>

      <!-- 运单详情 -->
      <WaybillDetail :id="carInfo.wayBillId" v-model:visible="detailVisible" />
    </div>
  </div>
</template>

<script lang="ts" setup>
import { wgs84ToGCJ02, toGCJ02PointArr } from '@/utils/mapUtil.ts'
import { useRequest } from '@/hooks/useRequest'
import { debounce } from 'lodash-es'
import { useMap } from '@/hooks/useMap'
import { VehicleStatusEnum, ShapeTypeEnum } from './enums'
import { statusOptions, type StatusOptions } from './options'
import { getVehicleList, getCarInfo, getCustomerList } from '@/api/vehicle-monitor/vehicleMonitor'
import type { VehicleListQuery, VehicleInfo, CarInfoQuery, CarInfoData, CustomerData, RailData } from '@/api/vehicle-monitor/vehicleMonitor'
import { getMixingStationList, type MixingStationData } from '@/api/customer/customer'
import WaybillDetail from '@/views/waybill/detail.vue'
import Storage from '@/utils/Storage'
import { DEFAULT_MIXING_STATION_ID_KEY } from '@/utils/constant'

interface StatusList extends StatusOptions {
  count?: number
}

// 车辆状态
const vehicleStatusList = ref<StatusList[]>([...statusOptions])
const carList = ref<VehicleInfo[]>([])
const carData = ref<VehicleInfo[]>([])

const searchValue = ref<string>('')
const queryParams = reactive<VehicleListQuery>({})

const carInfo = reactive<CarInfoData>({})
const detailVisible = ref(false)

const isLoading = ref(false)

const { map, AMap, initMap } = useMap('map-container', { manual: true })

const init = async () => {
  await runMixingStationList()
  if (!mixingStationOptions.value.length) return

  let defaultMixingStation: MixingStationData = null
  const defaultMixingStationId = Storage.getStorage(DEFAULT_MIXING_STATION_ID_KEY)

  // 先从本地缓存匹配搅拌站数据；如果能找到数据，则说明该用户有本地缓存搅拌站的权限
  if (defaultMixingStationId) {
    defaultMixingStation = mixingStationOptions.value.find(item => item.id === defaultMixingStationId)
  }

  // 如果无缓存或者无法匹配到数据；则默认取第一个数据并重新设置缓存
  if (!defaultMixingStation) {
    defaultMixingStation = mixingStationOptions.value[0]
    Storage.setStorage(DEFAULT_MIXING_STATION_ID_KEY, defaultMixingStation.id)
  }

  queryParams.mixingStationId = defaultMixingStation.id
  queryParams.mixingStationNumber = defaultMixingStation.number
  queryParams.mixingStationName = defaultMixingStation.name

  await initMap()
  initOverlayGroup()
  createInfoWindowInstance()
  getVehicleData()
  getCustomerData()

  // 地图监听点击事件，隐藏上一次已选择的客户围栏文本覆盖物
  map.value.on('click', () => hideLastRailTextOverlay())
}

// 获取搅拌站下拉数据
const {
  loading: mixingStationLoading,
  data: mixingStationData,
  runAsync: runMixingStationList
} = useRequest(getMixingStationList, { manual: true })

const mixingStationOptions = computed(() => mixingStationData.value?.result || [])

// 选择搅拌站
const handleSelectMixingStation = (id: string) => {
  handleCloseInfoWindow()
  const mixingStationOption = mixingStationOptions.value.find(item => item.id === id)

  if (mixingStationOption) {
    Storage.setStorage(DEFAULT_MIXING_STATION_ID_KEY, mixingStationOption.id)
    queryParams.mixingStationName = mixingStationOption.name
    queryParams.mixingStationNumber = mixingStationOption.number
    currentTrackCardId.value = '' //切换搅拌站，停止跟踪车辆
    getVehicleData()
    getCustomerData()
  }
}

const { loading: carListLoading, runAsync: runVehicleList } = useRequest(getVehicleList, { manual: true })

/**
 * 获取站点车辆列表
 * @param isFitView 地图是否窗口自适应，默认为true
 */
const getVehicleData = async (isFitView: boolean = true) => {
  isLoading.value = true
  try {
    const { result } = await runVehicleList({ ...queryParams })
    // 处理车辆状态统计
    for (const item of vehicleStatusList.value) {
      switch (item.value) {
        case VehicleStatusEnum.InTransit:
          item.count = result.inTransitCarCount
          break
        case VehicleStatusEnum.BeQueuing:
          item.count = result.waitInLineCarCount
          break
        case VehicleStatusEnum.Leisure:
          item.count = result.freeCarCount
          break
      }
    }

    const ownCars = result.ownCars ?? []

    carList.value = ownCars
    carData.value = ownCars

    const markers = renderMarker(ownCars)
    generateCarOverlayGroup(markers)
    if (markers.length > 0) {
      // 如果正在跟踪车辆，地图中心点定位到该车辆
      // if (currentTrackCardId.value) {
      //   const cars = ownCars.filter(item => item.carId === currentTrackCardId.value)
      //   cars.length > 0 && map.value.setCenter(cars[0].lnglat, true)
      // } else {
      //   // isFitView && map.value.setFitView(markers, true, [120, 120, 120, 120])
      //   isFitView && map.value.setFitView(markers)
      // }

      // 车辆信息窗打开状态表示正在跟踪车辆，更新窗口信息和位置；todo: 车辆信息窗实现了车辆跟踪方法，还没有场景使用，先保留
      if (infoWindowInstance && infoWindowInstance.getIsOpen()) {
        const extData: VehicleInfo | null = infoWindowInstance.getExtData()
        if (extData) {
          const cars = ownCars.filter(item => item.carId === extData.carId)
          cars.length > 0 && fetchCarInfo(cars[0])
        }
      }
      // 需求改为同时看所有车辆位置，不需要限制范围
      // isFitView && map.value.setFitView(markers, true, [120, 120, 120, 120])
      isFitView && map.value.setFitView(markers)
    }

    setTimeout(() => {
      isLoading.value = false
    }, 500)
  } catch {
    isLoading.value = false
  }
}

// 获取车辆详情
const fetchCarInfo = (data: VehicleInfo) => {
  if (!data.lnglat) {
    handleCloseInfoWindow()
    carInfo.deviceId = ''
    ElMessage.warning('该车辆暂无定位信息！')
    return
  }

  const param: CarInfoQuery = { deviceId: data.deviceId, carNumber: data.carNumber, mixingStationNumber: queryParams.mixingStationNumber }

  getCarInfo(param).then(async ({ result }) => {
    Object.assign(carInfo, result)
    await nextTick()

    map.value.setCenter(data.lnglat, true)
    infoWindowInstance.setExtData(data)
    infoWindowInstance.open(map.value, data.lnglat)
  })
}

// 车辆跟踪
function trackVehicle(carId: string) {
  if (!carId) {
    // 停止跟踪车辆
    currentTrackCardId.value = ''
    return
  }
  const cars = carList.value.filter(item => item.carId === carId)
  if (cars.length > 0) {
    currentTrackCardId.value = carId
  }
}

// 车辆列表搜素
const onQueryChanged = debounce(() => {
  if (searchValue.value === '') carData.value = carList.value

  carData.value = carList.value.filter(item => {
    const str = item.mixingTruckNumber + '-' + item.carNumber
    return str.indexOf(searchValue.value) !== -1
  })
}, 300)

onMounted(async () => {
  init()
})

// 车辆自动刷新（间隔30秒）
let setIntervalId = setInterval(() => getVehicleData(false), 30 * 1000)
// 客户列表自动刷新（间隔10分钟）
let setCustomerIntervalId = setInterval(() => getCustomerData(true), 10 * 60 * 1000)

onUnmounted(() => {
  // 销毁定时任务
  clearInterval(setIntervalId)
  setIntervalId = null
  clearInterval(setCustomerIntervalId)
  setCustomerIntervalId = null
  overlayGroup = null
  carOverlayGroup = null
  railOverlayGroup = null
  infoWindowInstance = null
})

// ============= 客户列表相关 ===================
const customerList = ref<CustomerData[]>([])
const customerData = ref<CustomerData[]>([])
const customerSearchValue = ref('')

let lastRailTextOverlayArr = [] // 记录上一次已选择的客户围栏文本覆盖物

const { loading: customerListLoading, runAsync: runCustomerList } = useRequest(getCustomerList, { manual: true })

/**
 * 获取客户列表
 * @param isReFresh 是否为定时器刷新，若是则需保留数据勾选状态，默认为false
 */
const getCustomerData = async (isReFresh: boolean = false) => {
  isLoading.value = true
  const lastCustomerList = customerList.value
  try {
    const { result } = await runCustomerList({ ...queryParams })

    result.forEach(item => {
      item.checked = true
      if (!isReFresh) return

      const findCustomerOption = lastCustomerList.find(ele => ele.customerId === item.customerId)

      if (!findCustomerOption) return
      item.checked = findCustomerOption.checked
      item.selected = findCustomerOption.selected
    })

    customerList.value = result
    customerData.value = result

    drawRailData()

    setTimeout(() => {
      isLoading.value = false
    }, 500)
  } catch {
    isLoading.value = false
  }
}

// 客户列表搜索
const onCustomerQueryChanged = debounce(() => {
  if (customerSearchValue.value === '') customerData.value = customerList.value

  customerData.value = customerList.value.filter(item => {
    const str = item.customerName + item.projectName
    return str.indexOf(customerSearchValue.value) !== -1
  })
}, 300)

// 画客户列表关联的围栏
function drawRailData() {
  const overlayGroupTempArr = []

  lastRailTextOverlayArr = []

  const option = {
    cursor: 'pointer',
    fillColor: '#ea6b58',
    strokeColor: '#ea6b58'
  }

  customerList.value.forEach(customerItem => {
    customerItem.railDataList.forEach(item => {
      let overlay = null

      const centerPoint = wgs84ToGCJ02(item.lng, item.lat)
      item.lng = centerPoint[0]
      item.lat = centerPoint[1]

      if (item.shapeType === ShapeTypeEnum.Circle) {
        overlay = new AMap.value.Circle({
          center: [item.lng, item.lat],
          radius: item.radius, //半径
          ...option
        })
      } else if (item.shapeType === ShapeTypeEnum.Rectangle) {
        const railData = toGCJ02PointArr(JSON.parse(item.railData))
        const bounds = new AMap.value.Bounds(railData[0][0], railData[0][2])
        overlay = new AMap.value.Rectangle({
          bounds,
          ...option
        })
      } else if (item.shapeType === ShapeTypeEnum.Polygon) {
        let path = toGCJ02PointArr(JSON.parse(item.railData))
        if (item.railType === 1) {
          for (let i = 0; i < path.length; i++) {
            path[i] = [path[i]]
          }
        } else {
          path = path[0]
        }
        overlay = new AMap.value.Polygon({
          path,
          ...option
        })
      }
      if (overlay) {
        const { customerId, customerName, projectName, checked, selected } = customerItem
        const railInfo = { customerId, customerName, projectName, ...item }

        // 生成围栏文本覆盖物
        const railTextOverlay = createRailTextOverlay(railInfo)

        // 创建该围栏覆盖物集合，文本覆盖物和围栏覆盖物绑定该集合
        const railOverlayGroupTemp = new AMap.value.OverlayGroup()
        railOverlayGroupTemp.addOverlays([overlay, railTextOverlay])

        if (!checked) railOverlayGroupTemp.hide()
        if (checked && selected) {
          railTextOverlay.show()
          lastRailTextOverlayArr.push(railTextOverlay)
        }

        // 添加该围栏覆盖物集合事件，实现文本显示隐藏
        railOverlayGroupTemp.on('mouseover', () => railTextOverlay.show())
        railOverlayGroupTemp.on('mouseout', () => railTextOverlay.hide())

        //设置该围栏覆盖物集合额外数据（为客户列表复选框选择使用）
        railOverlayGroupTemp.extData = railInfo

        // 将该围栏覆盖物集合添加到数组
        overlayGroupTempArr.push(railOverlayGroupTemp)
      }
    })
  })

  // 将所有围栏覆盖物添加到围栏覆盖物集合
  generateRailOverlayGroup(overlayGroupTempArr)
}

// 创建围栏文本覆盖物
function createRailTextOverlay(railInfo: RailData) {
  const center = new AMap.value.LngLat(railInfo.lng, railInfo.lat)

  const content = ` <div class="relative left-[-50%] top-[-50px] bg-gray-400 text-white whitespace-nowrap text-12px w-auto border-rd-2px px-5px py-2px">
        <div class="flex flex-col"><span>${railInfo.customerName}</span><span>${railInfo.projectName}</span></div>
        <div class="text-arrows"></div>
      </div>
    `

  return new AMap.value.Text({ text: content, position: center, visible: false })
}

// 勾选或取消勾选复选框操作事件
const handleCheckedChange = (val, customerData: CustomerData) => {
  const railOverlayGroupArr = railOverlayGroup.getOverlays()

  for (let index = 0; index < railOverlayGroupArr.length; index++) {
    const railOverlay = railOverlayGroupArr[index]

    const railInfo: RailData = railOverlay.extData
    if (railInfo.customerId !== customerData.customerId) continue

    if (val) {
      railOverlay.show()
      const railTextOverlay = railOverlay.getOverlays().find(overlay => overlay.type === 'AMap.Text')
      if (!customerData.selected) railTextOverlay.hide()
    }
    if (!val) railOverlay.hide()
  }
}

// 隐藏上一次已选择的客户围栏文本覆盖物
function hideLastRailTextOverlay() {
  if (!lastRailTextOverlayArr.length) return

  lastRailTextOverlayArr.forEach(lastRailTextOverlay => {
    lastRailTextOverlay.hide()
  })

  lastRailTextOverlayArr = []
}

// 选中或取消选中客户列表操作
const handleSelectCustomer = (customerData: CustomerData) => {
  hideLastRailTextOverlay()

  if (customerData.selected) {
    // 取消选中
    customerData.selected = false
    return
  }

  // 选中
  customerList.value.map(item => (item.selected = false))
  // 判断该客户没有围栏给提示
  if (!customerData.railDataList || !customerData.railDataList.length) {
    ElMessage.warning('该客户暂无围栏信息！')
    return
  }

  customerData.selected = true

  // 如果该客户未勾选，则不需执行下面逻辑
  if (!customerData.checked) return

  const railOverlayGroupArr = railOverlayGroup.getOverlays()

  // 过滤出绑定客户的围栏覆盖物集合
  const customerRailOverlayGroupArr = railOverlayGroupArr.filter(railOverlay => railOverlay.extData.customerId === customerData.customerId)

  let customerRailOverlays = [] // 自适应窗口覆盖物集合

  customerRailOverlayGroupArr.forEach(railOverlay => {
    const customerRailOverlayArr = railOverlay.getOverlays()

    customerRailOverlays = customerRailOverlays.concat(...customerRailOverlayArr)

    // 显示对应围栏文本
    const railTextOverlay = customerRailOverlayArr.find(overlay => overlay.type === 'AMap.Text')
    railTextOverlay.show()

    lastRailTextOverlayArr.push(railTextOverlay)
  })

  map.value.setFitView(customerRailOverlays, true, [60, 60, 60, 60], 15)
}

// ============= 地图相关 ===================
let marker = null // 地图检索marker点
let infoWindowInstance = null // 地图弹框实例
let overlayGroup = null // 覆盖物集合，对实例集合做整体操作
let carOverlayGroup = null // 车辆覆盖物集合
let railOverlayGroup = null // 围栏覆盖物集合
const currentTrackCardId = ref('') // 当前跟踪车辆的id
const carInfoWindowRef = ref(null)
const markerRef = ref(null)
const keyword = ref('')

// https://lbs.amap.com/api/javascript-api-v2/documentation#overlaygroup
// 生成车辆图标覆盖物集合
function generateCarOverlayGroup(overlays: any[]) {
  carOverlayGroup.clearOverlays()
  carOverlayGroup.addOverlays(overlays)
}

// 生成围栏覆盖物集合
function generateRailOverlayGroup(overlays: any[]) {
  railOverlayGroup.clearOverlays()
  railOverlayGroup.addOverlays(overlays)
}

// 渲染车辆 markers
function renderMarker(carDatas: VehicleInfo[]) {
  const markers = []

  carDatas.forEach((el: VehicleInfo) => {
    if (!el.lng || !el.lat) return
    el.lnglat = wgs84ToGCJ02(el.lng, el.lat)

    const vehicleColor = statusOptions.find(item => item.value === el.carStatus)?.color

    const content = `<div class="relative">
      <div class="absolute left-50% top-[-22px] bg-gray-400 text-white whitespace-nowrap text-12px w-auto border-rd-2px px-5px py-2px">
        <div class="flex">${el.mixingTruckNumber}-${el.carNumber}</div>
        <div class="label-arrows"></div>
      </div>
      <svg width="32" height="32" fill="${vehicleColor}"><use xlink:href="#truck"></use></svg>
    </div>
    `
    const marker = new AMap.value.Marker({
      content: content, //自定义点标记覆盖物内容
      position: el.lnglat, //基点位置
      offset: new AMap.value.Pixel(-17, -17), //相对于基点的偏移位置
      extData: el
    })

    marker.on('click', async function () {
      const data: VehicleInfo = marker.getExtData()
      data && fetchCarInfo(data)
    })

    markers.push(marker)
  })
  return markers
}

// 初始化地图覆盖物集合
function initOverlayGroup() {
  overlayGroup = new AMap.value.OverlayGroup()
  carOverlayGroup = new AMap.value.OverlayGroup() // 初始化车辆覆盖物集合
  railOverlayGroup = new AMap.value.OverlayGroup() // 初始化围栏覆盖物集合

  overlayGroup.addOverlays([carOverlayGroup, railOverlayGroup])
  map.value.add(overlayGroup)
}

// 生成地图弹框实例
function createInfoWindowInstance() {
  infoWindowInstance = new AMap.value.InfoWindow({
    isCustom: true, //使用自定义窗体
    // autoMove: false,
    content: carInfoWindowRef.value.$el,
    offset: new AMap.value.Pixel(0, -25)
  })
}

// 关闭地图弹框
function handleCloseInfoWindow() {
  if (infoWindowInstance) {
    infoWindowInstance.setExtData(null)
    infoWindowInstance.close()
  }
}

// 地址检索
function handleSearch(queryString, cb) {
  const placeSearch = new AMap.value.PlaceSearch({
    city: '',
    pageSize: 10, // 单页显示结果条数
    pageIndex: 1, // 页码
    citylimit: false // 是否强制限制在设置的城市内搜索
  })
  placeSearch.search(queryString, (status, result) => {
    if (result && typeof result === 'object' && result.poiList) {
      const list = result.poiList.pois
      list.forEach(item => {
        item.value = item.name
        item.label = item.name
      })
      cb(list)
    } else {
      cb([])
    }
  })
}
// 地址选择
function handleSelect(item) {
  if (marker) clearMarker()

  marker = new AMap.value.Marker({
    position: item.location,
    content: markerRef.value.$el,
    offset: new AMap.value.Pixel(-13, -30)
  })

  map.value.add(marker)
  map.value.setFitView(marker)
}
// 清除标记点
const clearMarker = () => {
  map.value.remove(marker)
  marker = null
}
</script>
<style lang="scss" scoped>
.left {
  background-color: var(--el-bg-color-overlay);
}

.wrapper_item {
  color: var(--el-text-color-regular);
  font-size: var(--el-font-size-extra-small);
  &:hover {
    color: var(--el-color-primary);
    background-color: var(--el-fill-color-light);
    .time-box {
      color: var(--el-color-primary);
    }
    .project-name-text {
      color: var(--el-color-primary);
    }
  }
}

.item-selected {
  color: var(--el-color-primary);
  background-color: var(--el-fill-color-light);
  .project-name-text {
    color: var(--el-color-primary);
  }
}

.project-name-text {
  color: var(--el-text-color-secondary);
  font-size: 12px;
}

.status-items {
  position: absolute;
  top: 10px;
  left: 15px;
  border-radius: 4px;
  height: 32px;
  display: flex;
  align-items: center;
  padding: 5px 0px;
  box-sizing: border-box;
  background-color: var(--el-bg-color-overlay);
  .status-item {
    padding: 0 10px;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    font-size: var(--el-font-size-base);
    > i {
      width: 8px;
      height: 8px;
      border-radius: 2px;
      margin-right: 6px;
    }
    > span {
      margin-left: 10px;
      font-weight: 600;
    }
  }
}
.tabs :deep(.el-tabs__nav) {
  padding-left: 20px;
  &::before {
    content: '';
    width: 3px;
    height: 13px;
    position: absolute;
    top: 50%;
    left: 0;
    transform: translateY(-50%);
    background-color: var(--el-color-primary);
  }
}
.tabs :deep(.el-tab-pane) {
  height: 100%;
}
</style>

<style>
.amap-overlay-text-container {
  background-color: transparent !important;
  padding: 0px !important;
  border: none !important;
  overflow: visible !important;
}

.label-arrows {
  width: 0;
  height: 0;
  border: 5px solid transparent;
  border-left-width: 5px;
  border-right-width: 5px;
  border-top-width: 18px;
  border-top-color: #9ca3af;
  position: absolute;
  left: -4px;
  bottom: -14px;
  transform: rotate(25deg);
}

.text-arrows {
  width: 0;
  height: 0;
  border: 5px solid transparent;
  border-left-width: 8px;
  border-right-width: 8px;
  border-top-width: 10px;
  border-top-color: #9ca3af;
  position: absolute;
  left: 50%;
  bottom: -14px;
  transform: translateX(-50%);
}

.loader-spinner {
  position: absolute;
  top: 10px;
  left: 280px;
  width: 32px;
  height: 32px;
  border: 4px solid #ddd5d5;
  border-top: 4px solid #3a75ff;
  border-left: 4px solid #3a75ff;
  border-radius: 50%;
  animation: spin 0.5s linear infinite;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
</style>
